<?php
/**
 * ModuleHookComponent allow modules to react to events in a loose manner
 * 
 * @uses CApplicationComponent
 * @package core
 * @version $Id: ModuleHookComponent.php 50 2010-05-01 12:10:33Z flavius.as $
 * @copyright 2010 &copy; Flavius Aspra
 * @author Flavius Aspra <flav@yet-another-project.com> 
 * @license {@link http://yii-modules.yet-another-project.com/licence}
 */
class ModuleHookComponent extends CApplicationComponent {
	/**
	 * hooksDirectory the name of the directory where hooks lie
	 * 
	 * @var string
	 * @access public
	 */
	public $hooksDirectory = 'hooks';
	/**
	 * init initializes the modulehook component and raises the event
	 * application.components.ModuleHookComponent.init
	 *
	 * @access public
	 * @return void
	 * @raises application.components.ModuleHookComponent.init
	 */
	public function init() {
		parent::init();
		if($this->getIsInitialized()) {
			$ev = new ExtEvent($this,FALSE);
			$this->raiseEvent('application.components.ModuleHookComponent.init',$ev);
		}
	}
	/**
	 * raiseEvent trigger a hook for all application modules
	 * 
	 * @param string $name the name of the event
	 * @param ExtEvent $ev the event
	 * @access public
	 * @return void
	 */
	public function raiseEvent($name,$ev) {
		if($ev instanceof ExtEvent && !$ev->tryDefault) {
			$this->runHooks($name,$ev);
		}
		else {
			try {
				parent::raiseEvent($name,$ev);
			}
			catch(CException $e) {
				$this->runHooks($name,$ev);
			}
		}
	}
	public function runHooks($name,&$ev) {
		if($ev->controller) {
			$ctrl = $ev->controller;
		}
		else {
			$ctrl = Yii::app()->controller;
		}
		$module = $ev->module;
		$action = $ev->action;
		if(is_object($ctrl)) {
			$module = $ctrl->module;
			$action = $ctrl->getAction();
		}
		if(is_array($ev->for)) {
			$hooks = $this->getHooks($name,$ev->for);
		}
		else {
			$hooks = $this->getHooks($name);
		}
		foreach($hooks as $hooked => $h) {
			$this->runHook($h,array(
				'event' => &$ev,
				'module' => $module,
				'isActive' => $hooked === $module,
				'controller' => $ctrl,
				'action' => $action
			));
		}
	}
	/**
	 * runHook actually run a hook
	 * 
	 * @param string $self the hook file
	 * @param mixed $args parameters to pass to the hook, just as with a regular view
	 * @access public
	 * @return whatever the included hook file returns
	 */
	public function runHook($self,$args=NULL) {
		$args = (array) $args;
		extract($args);
		unset($args);
		return include $self;
	}
	/**
	 * getHooks get the hooks files for a given hook name for all the modules
	 * 
	 * @param mixed $name the name of the hook
	 * @param mixed $modules optional. defaults to all modules of the app. otherwise the name of the modules to get
	 * the right files for
	 * @access public
	 * @return an associative array of moduleId => hookFile
	 * @todo use some caching
	 */
	public function getHooks($name,$modules=NULL) {
		$t = array_keys(Yii::app()->getModules());
		if(NULL === $modules) {
			$modules = $t;
		}
		else {
			$modules = array_intersect($modules,$t);
		}
		$r = array();
		$mPath = Yii::app()->getModulePath();
		foreach($modules as $m) {
			//@todo use Controller::resolveViewFile() instead (?)
			$t = $mPath . DIRECTORY_SEPARATOR .$m.DIRECTORY_SEPARATOR.
				$this->hooksDirectory.DIRECTORY_SEPARATOR.$name.'.php';
			if(is_readable($t)) {
				$r[$m] = $t;
			}
			else {
				//TODO notify of possible errors if in development mode
			}
		}
		return $r;
	}
}
